package no.ntnu.idi.deid.documentunits;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Pattern;


import no.ntnu.idi.deid.config.Constants;
import no.ntnu.idi.deid.preprocessor.postagger.POSTag;
import no.ntnu.idi.deid.preprocessor.wordcount.CountManager;
import no.ntnu.idi.deid.searchresultunits.CategoryTag;
import no.ntnu.idi.deid.searchresultunits.Tag;

public class Token implements Iterable<CategoryTag>, IDocumentUnits {
	
	/*
	 * Roar - Note to self: infoToken, digitCount  burde settes i konstruktøren for å slippe nullsjekk og rydde opp i koden.
	 */
	
	// add result types
	private String originalToken;
	private String strippedToken; // originalToken without comma and dots in the end 
	private boolean infoToken; //ends with colon
	private int specialCharCount; //the amount of specialChars within the word. A character is a specialCgar if it is a non-digit and non-letter.
	private int digitCount; //number of digits in the token.
	private boolean isCleanWord; //true if token purely consists of letters. 
	private int index; //the index of this token 
	private Token[] innerTokens;
	private POSTag postag;
	private Sentence parent;
	private String stemmedToken;
	
	//**************Start of ExtendedToken*******************
	private Set<CategoryTag> searchResult;
	private boolean sensitivityTag = true;
	private boolean sensitivityTagDecided = false;
	private CategoryTag finalTag;
	
	//Constructor for Tokens
	public Token(int ind, String word) {
		originalToken = word;
		strippedToken = stripToken(originalToken);
		index = ind;
		specialCharCount = 0;
		digitCount = 0;
		setTokenLabels(word);
		innerTokens = checkMultipleWordsWithinToken(strippedToken);
		stemmedToken = "";
		searchResult = new HashSet<CategoryTag>();
	}
	
	//Constructor for postags-tokens
	public Token(int ind, String word, boolean posToken){
		originalToken = word;
		index = ind;
	}
	
	public String getStemmedToken() {
		return stemmedToken;
	}

	public void setStemmedToken(String stemmedToken) {
		this.stemmedToken = stemmedToken;
	}
	
	public boolean hasStemmedToken(){
		if (stemmedToken.isEmpty()) {
			return false;
		}
		return true;
	}

	public boolean hasNext(){
		return getSentence().tokenHasNext(this);
	}
	
	public Token next(){
		return getSentence().getTokenWithIndex(getIndex()+1);
	}
	
	public boolean hasSearchResult(){
			return !searchResult.isEmpty();
	}
	
	public int searchResultSize() {
		return searchResult.size();
	}
	
	public boolean strippedTokenEquals(String str) {
		return strippedToken.equals(str);
	}
	
	public boolean containsMultipleWords() {
			return innerTokens.length>0;
	}



	public void setParent(Sentence parent) {
		this.parent = parent;
	}

	public Token[] getInnerWords() {
		return innerTokens;
	}

	public Set<CategoryTag> getCategoryTagSet() {
		return searchResult;
	}
	
	
	
	public Document getDocument() {
		return parent.getDocument();
	}
	
	public Section getSection() {
		return parent.getSection();
	}
	
	public Sentence getSentence() {
		return parent;
	}
	
	public boolean removeTag(Tag tag) {
		if(searchResult.contains(new CategoryTag(tag))) {
			Iterator<CategoryTag> searchIterator = iterator();
			while(searchIterator.hasNext()) {
				CategoryTag categoryTag = searchIterator.next();
				if(tag.equals(categoryTag.getTag())) {
					searchResult.remove(categoryTag);
				}
			}
			return searchResult.remove(tag);
		}
		return false;
	}
	
	public boolean addTag(Tag tag) {
		if(tag != null) {
			if(!searchResult.contains(tag)) {
				return searchResult.add(new CategoryTag(tag));
			}
		}
		return false;	
	}
	public boolean addTag(CategoryTag tag) {
		if(tag != null && !searchResult.contains(tag)) {
			return searchResult.add(tag);
		}
		return false;	
	}
	
	public void addSet(Set<CategoryTag> tags) {
		if(tags != null) {
			for(CategoryTag tag: tags)
				if(tag!=null && !searchResult.contains(tag)) {
					searchResult.add(tag);
				}
		}
		
	}
	
	public boolean containsCategoryTag(Tag tag) {
		if(searchResult.contains(new CategoryTag(tag))) {
			return true;
		}
		return false;
	}
	
	public boolean assignPosTag(Token posT){
		String tag = posT.toString().substring(toString().length());
		String posTag = tag.substring(1,tag.length()-3);

		if(posT.toString().substring(0, toString().trim().length()).equals(getOrginal())){
			setPOSTag(POSTag.setTagByValue(posTag));
			return true;
		}
		else{
			System.err.println("Failed to assign postag because " + posT.toString().substring(0,toString().trim().length())  + " and " + getOrginal() + " don't match");
			return false;
		}
	}
	
	public String toString(){
		return originalToken;
//		return  "originalToken " + originalToken + ", index: " + index + ", stripped: " + strippedToken + ", " + "infoToken: " + infoToken + ", specialChars: " + specialCharCount + ", digitcount: " + digitCount;
	}
	
	public String getOrginal(){
		return originalToken;
	}
	public String getStripped(){
		return strippedToken;
	}
	public boolean getInfoToken(){
		return infoToken;
	}
	public int getSpecialCharCount(){
		return specialCharCount; 
	}
	public int getDigitCount(){
		return digitCount;
	}
	public boolean isTokenClean(){
		return isCleanWord;
	}
	
	public int getIndex(){
		return index;
	}
	
	public POSTag getPOSTag(){
		return postag;
	}
	
	public void setPOSTag(POSTag tag){
		postag = tag;
	}
	
	public void setSensetivityTag(boolean b) {
		sensitivityTag = b;
		sensitivityTagDecided = true;
		
	}
	
	//************FINALIZING********************//
	public String getFinalString(){
		if(this.getStripped().equals("4")) {
			System.out.println();
		}
		String s = "";
		if(isSensitiv()){
			s = (finalTag==null)? Constants.UKNOWNTAG : "<"+finalTag.toString()+">";
			return s;
		}
		return originalToken;
	}
	
	public CategoryTag getFinalTag(){
		return finalTag;
	}
	
	
	public boolean isSensitiv() {
		return sensitivityTag;
	}
	
	public boolean isSensitivityTagDecided() {
		return sensitivityTagDecided;
		
	}
	
	//************FINALIZING********************//
	
	public void setFinalTag(CategoryTag finaltag) {
		this.finalTag = finaltag;
		setSensetivityTag(finaltag.isSensitive());
	}
	
	public void setFinalTag(Tag finaltag) {
		this.finalTag = new CategoryTag(finaltag);
		setSensetivityTag(finaltag.isSensitive());
	}
	
	public static String stripToken(String str){
		if(str.length()==1){
			return str;
			}
		else if(!Character.isLetterOrDigit(str.charAt(0))){
			return stripToken(str.substring(1));
			}
		else if(!Character.isLetterOrDigit(str.charAt(str.length()-1))){
			return stripToken(str.substring(0,str.length()-1));
			}
		else{
			return str;
			}
	}
	
	private void setTokenLabels(String token){
		infoToken = (token.endsWith(":"))? true: false;
		String tempToken = (infoToken) ? token.substring(0, token.length()-1): token;
		for (int i = 0; i < tempToken.length(); i++) {
			if(Character.isDigit(tempToken.charAt(i))){digitCount++;}
			if(!Character.isLetterOrDigit(tempToken.charAt(i))){specialCharCount++;}
			//Er dette nok for å sjekke om Char'et er en bokstav?
		}
		isCleanWord = ((digitCount==0)&&(specialCharCount==0))? true: false;

	}
	
	public static boolean isNumber(String s){
		boolean isNumber = true;
		for (int i = 0; i < s.length(); i++) {
			if(Character.isLetter(s.charAt(i))){
				isNumber = false;
			}
		}
		return isNumber;
	}
	
	/** This method is used in Document.class in order to split words like "dag/kveld/natt:" and returns [dag kveld natt]
	 * 
	 * @param s
	 */
	private Token[] checkMultipleWordsWithinToken(String string){
		Token[] innerTokenList = null;
		if(string.length()>=3){
			int specialChars = 0;
			int letters = 0;
				for(int i=0; i<string.length(); i++){
					if( Character.isLetter(string.charAt(i))){
						letters++;
					}
					if( !Character.isLetterOrDigit(string.charAt(i))){
						specialChars++;
					}
				}
				if(letters+specialChars==string.length() && letters>0 && specialChars>0){
//					String pattern = "\\\\|\\/|\\.|\\(|\\)|\\:|\\;|\\<|\\>|\\-|\\,|\\_|\\+|\\-|\\!|\\{|\\}|\\|";
					String pattern = "[^\\pL]";
					Pattern splitter = Pattern.compile(pattern);
					String[] res = splitter.split(string);
					ArrayList<Token> innerTokens = new ArrayList<>();
					for (int i = 0; i < res.length; i++) {
						if(!res[i].trim().isEmpty()){
							innerTokens.add(new Token(i, res[i]));
						}
					}
					if(innerTokens.size()>1){
						innerTokenList = innerTokens.toArray(new Token[innerTokens.size()]);
						return innerTokenList;
					}
					else{
						return new Token[0];
					}
				}
		}			
		return new Token[0];
	}

	@Override
	public Iterator<CategoryTag> iterator() {
		return searchResult.iterator();
	}
	
	public CountManager getCountManager() {
		return getDocument().getWordCountManager();
	}

	
	public CategoryTag getRandomSearchResultTag() {
		Iterator<CategoryTag> it = searchResult.iterator();
		double d = (Math.random() * (searchResult.size()));
		int value = (int)d;
		int counter = 0;
		CategoryTag tag = null;
		while(counter <= value) {
			tag = it.next();
			counter++;
		}
		return tag;		
	}

}
